direnvを使ってGoogle Cloudプロジェクトの認証情報を簡単に切り替えてみる
はじめに
データアナリティクス事業本部のkobayashiです。
Google Cloudで複数プロジェクトを取り扱っていて各プロジェクトのディレクトリに移動して作業しようとした際に認証情報が自動的に切り替えられたら便利だと思っていました。他クラウドの認証情報の切り替えでdirenvを使っていたので今回も同じようにdirenvで解決したのでその内容をまとめます。
direnvを使ってGoogle Cloudの認証情報を切り替える
direnvは、プロジェクトごとに環境変数を自動的に設定・解除するためのツールです。ディレクトリごとに異なる設定を行っておけば、そのディレクトリに入ると自動的に環境が設定され、ディレクトリを出ると環境が解除されます。これにより、異なるプロジェクト間での環境設定の衝突を防ぎ、開発環境をクリーンに保つことができます。
今回はこのツールを使ってGoogle Cloudで使う認証情報を切り替えます。
gcloud CLI 認証構成と ADC 構成
公式ドキュメント( gcloud CLI 認証構成と ADC 構成 | gcloud CLI を使用して認証する | Authentication | Google Cloud )に以下のように書かれています。
gcloud CLI にログインするときは、gcloud auth login コマンドを使用して、gcloud CLI に対するプリンシパルを認証します。gcloud CLI は、そのプリンシパルを使用して、Google Cloud のリソースとサービスを管理するための認証と認可を行います。これが gcloud CLI 認証構成です。
gcloud CLI を使用して ADC を構成する場合は、gcloud auth application-default login コマンドを使用します。このコマンドは、指定したプリンシパルを使用して、ローカル環境の ADC を構成します。これが ADC 構成です。
簡単に理解するとGoogle Cloudでは認証構成がgcloud CLI認証構成とADC構成の2つあり、gcloudコマンドを使った場合の認証構成とPythonやTerraform実行時に使われるADCのユーザー認証情報となります。
詳しくは公式ドキュメントをご確認ください。
gcloud CLI を使用して認証する | Authentication | Google Cloud
では早速設定を行っていきます。
環境
- macOS 13.4.1
- direnv 2.33.0
direnvの.envrc設定
今回最終的にやりたこととしてはプロジェクトのディレクトリに移動した際に自動的にgcloud CLI認証情報とADC構成を変更することです。
はじめに環境を設定しておきます。Google Cloudのプロジェクトとしてexample-project-a,example-project-b,example-project-c
の3つがあり、example-project-a,example-project-b
はkobayashi-1@example.com、example-project-c
はkobayashi-2@example.comのアカウントでアクセスします。
$ gcloud auth list
Credentialed Accounts
ACTIVE ACCOUNT
* kobayashi-1@example.com
kobayashi-2@example.com
$ gcloud config configurations list
NAME IS_ACTIVE ACCOUNT PROJECT COMPUTE_DEFAULT_ZONE COMPUTE_DEFAULT_REGION
project_a True kobayashi-1@example.com example-project-a asia-northeast1-a asia-northeast1
project_b False kobayashi-1@example.com example-project-b asia-northeast1-a asia-northeast1
project_c False kobayashi-2@example.com example-project-c asia-northeast1-a asia-northeast1
この状態で以下のようなディレクトリ構成を行います。
.
├── project_a
│ ├── .envrc
│ └── gcs_list.py
├── project_b
│ ├── .envrc
│ └── gcs_list.py
└── project_c
├── .envrc
└── gcs_list.py
gcs_list.pyは以下のような簡単なファイル、でプロジェクトのGCSバケットと使用している認証情報を表示するものになります。
import jwt
from google.cloud import storage
import google.auth
import google.auth.transport.requests
from google.oauth2.credentials import Credentials
from google.auth.exceptions import DefaultCredentialsError
def print_buckets():
client = storage.Client()
print("Bucket情報:")
for bucket in client.list_buckets():
print(bucket.name)
def print_default_credentials_info():
credentials, project = google.auth.default()
request = google.auth.transport.requests.Request()
credentials.refresh(request)
# IDトークンからメールアドレスを抽出
decoded = jwt.decode(credentials.id_token, options={"verify_signature": False})
email = decoded.get('email')
print("ADC情報:")
print(f"プロジェクトID: {project}")
print(f"アカウント: {email}")
# メイン処理
if __name__ == "__main__":
print_buckets()
print_default_credentials_info()
では.envrc
を設定していきます。gcloud CLI認証情報に関しては公式ドキュメント( gcloud CLI の構成の管理 | Google Cloud CLI Documentation )にある通りCLOUDSDK_ACTIVE_CONFIG_NAME
環境変数を使うことで簡単に切り替えることができます。
構成の変更をシームレスにするには、direnv や ondir などのツールを活用して、作業ディレクトリを切り替えるときに自動的に構成を切り替えることができます。この方法の 1 つとして、プロジェクトのルート ディレクトリにある .envrc ファイルに必要な環境変数(CLOUDSDK_ACTIVE_CONFIG_NAME など)を設定する方法があります。
export CLOUDSDK_ACTIVE_CONFIG_NAME={gcloud config configurations listのNAME}
上記の設定を行った後に各ディレクトリに移動して確かめてみます。
$ cd project_a
direnv: loading ~/project_a/.envrc
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
$ gcloud storage ls
gs://kobayashi-project-a/
$ gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = kobayashi-1@example.com
disable_usage_reporting = True
project = example-project-a
Your active configuration is: [project_a]
$ cd project_b
direnv: loading ~/project_b/.envrc
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
$ gcloud storage ls
gs://kobayashi-project-b/
$ cd project_c
direnv: loading ~/project_c/.envrc
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
$ gcloud storage ls
gs://kobayashi-project-c/
ただこの状態だとgcloud CLI認証情報は自動的に切り替わりますが、ADC認証情報は切り替わらないためPythonやterraformを実行してもディレクトリで設定してあるプロジェクトの認証情報で実行できません。そのためADC認証情報も自動的に変更するように.envrc
に設定を追加します。
export CLOUDSDK_ACTIVE_CONFIG_NAME={gcloud config configurations listのNAME}
# 以下追加
confirm_adc() {
local adc_file="$HOME/.config/gcloud/application_default_credentials.json"
local client_id=$(jq -r .client_id "$adc_file")
local client_secret=$(jq -r .client_secret "$adc_file")
local refresh_token=$(jq -r .refresh_token "$adc_file")
local token_response=$(curl -s -d "client_id=$client_id&client_secret=$client_secret&refresh_token=$refresh_token&grant_type=refresh_token" \
https://oauth2.googleapis.com/token)
local decode_token=$(echo $token_response | jq -r .id_token | cut -d"." -f2 | base64 -d)
local gcloud_email=$(gcloud config get-value account --quiet 2>/dev/null)
if [[ ! $decode_token == *"$gcloud_email"* ]]; then
echo "adcとgcloud configのアカウントが異なるのでadcの再設定をします"
gcloud auth application-default login
fi
}
confirm_adc
project_id=$(gcloud config get-value project --quiet 2>/dev/null)
email=$(gcloud config get-value account --quiet 2>/dev/null)
echo "Project ID: $project_id"
echo "Accounts Email: $email"
内容としてはgcloudのアカウントとADCのアカウントを確認して異なればgcloud auth application-default login
でADC構成を再設定するものになります。
ではdirenv allow .
を実行してdirenvの設定を反映してからディレクトを移動してみます。
$ cd project_a
direnv: loading ~/project_a/.envrc
adcとgcloud configのアカウントが異なるのでadcの再設定をします。
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=......
....
Project ID: example-project-a
Accounts Email: kobayashi-1@example.com
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
ディレクトリを移動した際にgcloudのアカウントとADCのアカウントが異なるとadcとgcloud configのアカウントが異なるのでadcの再設定をします。
が表示され以下のような画面が開きADC認証情報の再設定が行えます。
この状態でディレクトリ配下のpythonスクリプトを実行してみます。
$ python google_resources.py
Bucket情報:
kobayashi-project-a
ADC情報:
プロジェクトID: example-project-a
アカウント: kobayashi-1@example.com
切り替わったADC認証情報でPythonスクリプトが実行されています。
次に同じkobayashi-1@example.com
のアカウントを使ったproject_bのディレクトリに移動してみます。
$ cd ../project_b
direnv: loading ~/project_b/.envrc
Project ID: example-project-b
Accounts Email: kobayashi-1@example.com
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
$ python google_resources.py
Bucket情報:
kobayashi-project-b
ADC情報:
プロジェクトID: example-project-b
アカウント: kobayashi-1@example.com
同じkobayashi-1@example.com
のアカウントなのでADCの再構成が行われずそのままcdで設定を変更することができました。
次にkobayashi-2@example.com
のアカウントを使ったproject_cのディレクトリに移動してみます。
$ cd ../project_c
direnv: loading ~/project_c/.envrc
adcとgcloud configのアカウントが異なるのでadcの再設定をします。
Your browser has been opened to visit:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=......
....
Project ID: example-project-c
Accounts Email: kobayashi-2@example.com
direnv: export +CLOUDSDK_ACTIVE_CONFIG_NAME
$ python google_resources.py
Bucket情報:
kobayashi-project-c
ADC情報:
プロジェクトID: example-project-c
アカウント: kobayashi-2@example.com
すると今度はADCのアカウントが異なるのでADCの再構成となり、adcとgcloud configのアカウントが異なるのでadcの再設定をします。
が表示され以下のような画面が開きADC認証情報の再設定が行えます。
まとめ
Google Cloudで複数プロジェクトを扱っている場合にdirenvを使ってディレクトリ移動で簡単にgloud CLI構成とADC構成を切り替えてみました。
最後まで読んで頂いてありがとうございました。